/*
 * Copyright 2023, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * 龙芯3A6000 LS7A2000桥片 PCIe配置空间访问方法
 */

#include "loongson_pch_pci.h"

#include <KernelExport.h>
#include <PCI.h>
#include <SupportDefs.h>
#include <arch/cpu.h>
#include <vm/vm.h>
#include <vm/vm_page.h>
#include <vm/vm_types.h>

#define TRACE_LOONGSON_PCH_PCI
#ifdef TRACE_LOONGSON_PCH_PCI
#define TRACE(x...) dprintf("loongson_pch_pci: " x)
#else
#define TRACE(x...)
#endif

#define ERROR(x...) dprintf("loongson_pch_pci: " x)

// PCIe配置空间映射区域
static area_id sPCIConfigArea = -1;
static void *sPCIConfigBase = NULL;

// DMA地址映射相关
#define LS7A_DMA_MASK_36BIT 0xFFFFFFFFF // 36位物理地址掩码
#define LS7A_DMA_WINDOW_BASE 0x80000000 // DMA窗口基地址
#define LS7A_DMA_WINDOW_SIZE 0x80000000 // DMA窗口大小 (2GB)

// PCIe配置空间初始化
status_t
loongson_pch_pci_init(void)
{
    TRACE("Initializing LS7A2000 PCH PCI\n");

    // 映射PCIe配置空间
    sPCIConfigArea = map_physical_memory("LS7A PCIe config space",
                                         (phys_addr_t)LS7A_PCH_PCI_CONFIG_BASE,
                                         LS7A_PCH_PCI_CONFIG_SIZE,
                                         B_ANY_KERNEL_ADDRESS,
                                         B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
                                         &sPCIConfigBase);

    if (sPCIConfigArea < 0)
    {
        ERROR("Failed to map PCIe config space\n");
        return sPCIConfigArea;
    }

    TRACE("PCIe config space mapped at %p\n", sPCIConfigBase);
    return B_OK;
}

// PCIe配置空间卸载
void loongson_pch_pci_uninit(void)
{
    if (sPCIConfigArea >= 0)
    {
        delete_area(sPCIConfigArea);
        sPCIConfigArea = -1;
        sPCIConfigBase = NULL;
    }
}

// 获取配置空间地址
static inline void *
get_pci_config_address(uint8 bus, uint8 device, uint8 function, uint8 reg)
{
    if (sPCIConfigBase == NULL)
        return NULL;

    // 检查参数有效性
    if (device > 31 || function > 7)
        return NULL;

    // 计算配置空间地址
    uint32 offset = ((bus) << 16) | ((device) << 11) | ((function) << 8) | (reg);
    return (uint8 *)sPCIConfigBase + offset;
}

// 8位配置空间读取
uint8 loongson_pch_pci_read_config_8(uint8 bus, uint8 device, uint8 function, uint8 reg)
{
    void *address = get_pci_config_address(bus, device, function, reg);
    if (address == NULL)
        return 0xFF;

    return *(volatile uint8 *)address;
}

// 8位配置空间写入
void loongson_pch_pci_write_config_8(uint8 bus, uint8 device, uint8 function, uint8 reg, uint8 value)
{
    void *address = get_pci_config_address(bus, device, function, reg);
    if (address == NULL)
        return;

    *(volatile uint8 *)address = value;
}

// 16位配置空间读取
uint16
loongson_pch_pci_read_config_16(uint8 bus, uint8 device, uint8 function, uint8 reg)
{
    void *address = get_pci_config_address(bus, device, function, reg);
    if (address == NULL)
        return 0xFFFF;

    // 确保16位对齐
    if (reg & 1)
    {
        uint16 value;
        value = loongson_pch_pci_read_config_8(bus, device, function, reg);
        value |= (uint16)loongson_pch_pci_read_config_8(bus, device, function, reg + 1) << 8;
        return value;
    }

    return *(volatile uint16 *)address;
}

// 16位配置空间写入
void loongson_pch_pci_write_config_16(uint8 bus, uint8 device, uint8 function, uint8 reg, uint16 value)
{
    void *address = get_pci_config_address(bus, device, function, reg);
    if (address == NULL)
        return;

    // 确保16位对齐
    if (reg & 1)
    {
        loongson_pch_pci_write_config_8(bus, device, function, reg, value & 0xFF);
        loongson_pch_pci_write_config_8(bus, device, function, reg + 1, value >> 8);
        return;
    }

    *(volatile uint16 *)address = value;
}

// 32位配置空间读取
uint32
loongson_pch_pci_read_config_32(uint8 bus, uint8 device, uint8 function, uint8 reg)
{
    void *address = get_pci_config_address(bus, device, function, reg);
    if (address == NULL)
        return 0xFFFFFFFF;

    // 确保32位对齐
    if (reg & 3)
    {
        uint32 value;
        value = loongson_pch_pci_read_config_16(bus, device, function, reg);
        value |= (uint32)loongson_pch_pci_read_config_16(bus, device, function, reg + 2) << 16;
        return value;
    }

    return *(volatile uint32 *)address;
}

// 32位配置空间写入
void loongson_pch_pci_write_config_32(uint8 bus, uint8 device, uint8 function, uint8 reg, uint32 value)
{
    void *address = get_pci_config_address(bus, device, function, reg);
    if (address == NULL)
        return;

    // 确保32位对齐
    if (reg & 3)
    {
        loongson_pch_pci_write_config_16(bus, device, function, reg, value & 0xFFFF);
        loongson_pch_pci_write_config_16(bus, device, function, reg + 2, value >> 16);
        return;
    }

    *(volatile uint32 *)address = value;
}

// DMA地址映射 - 支持36位物理地址
phys_addr_t
loongson_pch_pci_map_dma_memory(phys_addr_t physical_address, size_t size)
{
    // 检查地址是否已经在DMA窗口内
    if (physical_address < LS7A_DMA_WINDOW_SIZE)
    {
        // 地址已经在DMA窗口内，直接返回
        return physical_address;
    }

    // 检查地址是否超过36位
    if (physical_address > LS7A_DMA_MASK_36BIT)
    {
        ERROR("Physical address exceeds 36-bit limit: %#" B_PRIxPHYSADDR "\n", physical_address);
        return 0;
    }

    // 检查大小是否超过DMA窗口
    if (size > LS7A_DMA_WINDOW_SIZE)
    {
        ERROR("DMA mapping size exceeds window size: %#" B_PRIxSIZE "\n", size);
        return 0;
    }

    // 计算DMA映射地址 - 使用简单的偏移映射方法
    // 在实际实现中，可能需要更复杂的映射表和内存管理
    phys_addr_t dma_address = physical_address & (LS7A_DMA_WINDOW_SIZE - 1);

    TRACE("Mapped physical address %#" B_PRIxPHYSADDR " to DMA address %#" B_PRIxPHYSADDR "\n",
          physical_address, dma_address);

    return dma_address;
}

// 取消DMA地址映射
void loongson_pch_pci_unmap_dma_memory(phys_addr_t mapped_address, size_t size)
{
    // 在当前简单实现中，不需要特殊的取消映射操作
    // 在更复杂的实现中，这里可能需要释放映射表项或其他资源
    TRACE("Unmapped DMA address %#" B_PRIxPHYSADDR " size %#" B_PRIxSIZE "\n",
          mapped_address, size);
}